package org.xbl.xchain.sdk;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.xbl.xchain.sdk.amino.Codec;
import org.xbl.xchain.sdk.block.BlockInfo;
import org.xbl.xchain.sdk.block.BlockMetas;
import org.xbl.xchain.sdk.module.auth.msg.MsgTransfer;
import org.xbl.xchain.sdk.module.celerain.msg.MsgCelerain;
import org.xbl.xchain.sdk.module.consensus.msg.MsgCreateValidator;
import org.xbl.xchain.sdk.module.consensus.msg.MsgEditValidator;
import org.xbl.xchain.sdk.module.consensus.querier.ValidatorInfo;
import org.xbl.xchain.sdk.module.consensus.types.Description;
import org.xbl.xchain.sdk.module.cross.msg.MsgCrossFromOrigin;
import org.xbl.xchain.sdk.module.gov.msg.MsgVoidProposal;
import org.xbl.xchain.sdk.module.gov.msg.MsgVote;
import org.xbl.xchain.sdk.module.gov.querier.ProposalInfo;
import org.xbl.xchain.sdk.module.gov.types.VoteType;
import org.xbl.xchain.sdk.module.iccp.msg.MsgAppChain;
import org.xbl.xchain.sdk.module.iccp.types.AppChainInfo;
import org.xbl.xchain.sdk.module.iccp.types.AppChainOperationEnum;
import org.xbl.xchain.sdk.module.wasm.querier.ContractHistory;
import org.xbl.xchain.sdk.tx.TxInfo;
import org.xbl.xchain.sdk.exception.WrongQueryParamException;
import org.xbl.xchain.sdk.module.auth.querier.AuthAccount;
import org.xbl.xchain.sdk.module.auth.types.Token;
import org.xbl.xchain.sdk.module.wasm.msg.*;
import org.xbl.xchain.sdk.module.wasm.querier.ContractInfo;
import org.xbl.xchain.sdk.module.wasm.types.ContractFileUtil;
import org.xbl.xchain.sdk.module.member.msg.*;
import org.xbl.xchain.sdk.module.member.querier.*;
import org.xbl.xchain.sdk.module.member.querier.OrgInfo;
import org.xbl.xchain.sdk.module.member.types.ExpElement;
import org.xbl.xchain.sdk.module.member.types.PermissionPolicy;
import org.xbl.xchain.sdk.querier.*;
import org.xbl.xchain.sdk.types.*;
import org.xbl.xchain.sdk.crypto.algo.*;
import org.xbl.xchain.sdk.utils.GenesisUtils;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

import static org.xbl.xchain.sdk.querier.RPCPath.HEALTH;

@Data
public class XchainClient implements Cloneable {

    private SysConfig sysConfig;
    private Submitter submitter;
    private Querier querier;
    private TxConfig txConfig;

    public XchainClient() {
        this(new SysConfig());
    }

    public XchainClient(SysConfig sysConfig) {
        this.sysConfig = sysConfig;
        submitter = new Submitter(sysConfig);
        querier = new Querier(sysConfig);
        txConfig = new TxConfig();
    }

    public XchainClient WithTxConfig(TxConfig txConfig) throws CloneNotSupportedException {
        XchainClient clone = (XchainClient) this.clone();
        clone.setTxConfig(txConfig);
        return clone;
    }

    public XchainClient(String sdkUrl) {
        this(new SysConfig(sdkUrl));
    }

    public XchainClient(String sdkUrl, String chainId) {
        this(new SysConfig(sdkUrl, chainId));
    }

    public XchainClient(String sdkUrl, String chainId, String version) {
        this(new SysConfig(sdkUrl, chainId, version));
    }

    public XchainClient(String sdkUrl, String chainId, AlgorithmType cryptoType, String mainPrefix) {
        this(new SysConfig(sdkUrl, chainId, mainPrefix, cryptoType));
    }

    public XchainClient(String sdkUrl, String chainId, AlgorithmType cryptoType) {
        this(new SysConfig(sdkUrl, chainId, cryptoType));
    }

    public XchainClient(String sdkUrl, String chainId, String serverCertPath, AlgorithmType cryptoType) {
        this(new SysConfig(sdkUrl, chainId, cryptoType));
        this.sysConfig.setServerCertPath(serverCertPath);
    }

    public void setCryptoType(AlgorithmType cryptoType) {
        sysConfig.setDefaultCryptoType(cryptoType);
    }

    public void setMainPrefix(String mainPrefix) {
        sysConfig.setMainPrefix(mainPrefix);
    }

    /*******************************************************************************************************************************************************************/
    /*
     * member相关操作
     * */
    //新增账号
    public TxResponse addAccount(Account sender, String address, String orgFullId, String roleId) throws Exception {
        Msg msgAddAccount = null;
        switch (this.sysConfig.getVersion()) {
            case Version.SEQUENCE:
                msgAddAccount = new MsgAddAccountV1(address, orgFullId, roleId, sender.getKeyInfo().getAddress());
                break;
            default:
                msgAddAccount = new MsgAddAccount(address, orgFullId, roleId, sender.getKeyInfo().getAddress());
                break;
        }
        return submitter.submit(msgAddAccount, sender, txConfig);
    }

    //撤销账号（即删除账号，状态值置为3）
    public TxResponse revokeAccount(Account sender, String address) throws Exception {
        MsgRevokeAccount msgRevokeAccount = new MsgRevokeAccount(address, sender.getKeyInfo().getAddress());
        return submitter.submit(msgRevokeAccount, sender, txConfig);
    }

    //添加多个账号
    public TxResponse addMultipleAccounts(Account sender, List<MsgAddAccount> msgAddAccounts) throws Exception {
        if ("1".equals(this.sysConfig.getVersion())) {
            List<MsgAddAccountV1> msgAddAccountV1s = msgAddAccounts.stream().map(msgAddAccount -> new MsgAddAccountV1(msgAddAccount.getAddress(), msgAddAccount.getOrgFullId(), msgAddAccount.getRoleId(), msgAddAccount.getOwner())).collect(Collectors.toList());
            return submitter.submit(msgAddAccountV1s, sender, txConfig);
        } else {
            return submitter.submit(msgAddAccounts, sender, txConfig);
        }

    }

    //添加或删除用户角色
    public TxResponse editAccountRole(Account sender, String address, String roleId, MsgEditAccountRole.EditType editType) throws Exception {
        MsgEditAccountRole msgEditAccountRole = new MsgEditAccountRole(address, roleId, editType.getType(), sender.getKeyInfo().getAddress());
        return submitter.submit(msgEditAccountRole, sender, txConfig);
    }

    //冻结用户
    public TxResponse freezeAccount(Account sender, String address, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgFreezeAccount msgFreezeAccount = new MsgFreezeAccount(address, sender.getKeyInfo().getAddress(), effectiveTime, voteEndTime);
        return submitter.submit(msgFreezeAccount, sender, txConfig);
    }

    //解冻用户
    public TxResponse unfreezeAccount(Account sender, String address, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgUnFreezeAccount msgUnFreezeAccount = new MsgUnFreezeAccount(address, sender.getKeyInfo().getAddress(), effectiveTime, voteEndTime);
        return submitter.submit(msgUnFreezeAccount, sender, txConfig);
    }

    //添加网关
    public TxResponse addGateway(Account sender, String address, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgAddGateway msgAddGateway = new MsgAddGateway(address, effectiveTime, voteEndTime, sender.getKeyInfo().getAddress());
        return submitter.submit(msgAddGateway, sender, txConfig);
    }

    //删除网关
    public TxResponse revokeGateway(Account sender, String address, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgRevokeGateway msgRevokeGateway = new MsgRevokeGateway(address, sender.getKeyInfo().getAddress(), effectiveTime, voteEndTime);
        return submitter.submit(msgRevokeGateway, sender, txConfig);
    }

    //添加网络管理员
    public TxResponse addNetworkAdmin(Account sender, String address, Integer power, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgAddNetworkAdmin msgAddNetworkAdmin = new MsgAddNetworkAdmin(address, power, effectiveTime, voteEndTime, sender.getKeyInfo().getAddress());
        return submitter.submit(msgAddNetworkAdmin, sender, txConfig);
    }

    //删除网络管理员
    public TxResponse revokeNetworkAdmin(Account sender, String address, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgRevokeNetworkAdmin msgRevokeNetworkAdmin = new MsgRevokeNetworkAdmin(address, sender.getKeyInfo().getAddress(), effectiveTime, voteEndTime);
        return submitter.submit(msgRevokeNetworkAdmin, sender, txConfig);
    }

    //添加组织
    public TxResponse addOrg(Account sender, String orgId, String orgAdminAddress, Boolean isNwAdmin, Integer power, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgAddOrg msgAddOrg = new MsgAddOrg(orgId, orgAdminAddress, isNwAdmin ? 1 : 0, power, effectiveTime, voteEndTime, sender.getKeyInfo().getAddress());
        return submitter.submit(msgAddOrg, sender, txConfig);
    }

    //删除组织
    public TxResponse revokeOrg(Account sender, String orgId, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgRevokeOrg msgRevokeOrg = new MsgRevokeOrg(orgId, sender.getKeyInfo().getAddress(), effectiveTime, voteEndTime);
        return submitter.submit(msgRevokeOrg, sender, txConfig);
    }

    //添加子组织
    public TxResponse addSubOrg(Account sender, String orgId, String parentOrgFullId, String orgAdminAddress) throws Exception {
        MsgAddSubOrg msgAddSubOrg = new MsgAddSubOrg(orgId, parentOrgFullId, orgAdminAddress, sender.getKeyInfo().getAddress());
        return submitter.submit(msgAddSubOrg, sender, txConfig);
    }

    //删除子组织
    public TxResponse revokeSubOrg(Account sender, String orgFullId) throws Exception {
        MsgRevokeSubOrg msgRevokeSubOrg = new MsgRevokeSubOrg(orgFullId, sender.getKeyInfo().getAddress());
        return submitter.submit(msgRevokeSubOrg, sender, txConfig);
    }

    //冻结组织
    public TxResponse freezeOrg(Account sender, String orgFullId) throws Exception {
        return freezeOrg(sender, orgFullId, null, null);
    }

    public TxResponse freezeOrg(Account sender, String orgFullId, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgFreezeOrg msgFreezeOrg = new MsgFreezeOrg(orgFullId, sender.getKeyInfo().getAddress(), effectiveTime, voteEndTime);
        return submitter.submit(msgFreezeOrg, sender, txConfig);
    }

    //解冻组织
    public TxResponse unfreezeOrg(Account sender, String orgFullId) throws Exception {
        return unfreezeOrg(sender, orgFullId, null, null);
    }

    public TxResponse unfreezeOrg(Account sender, String orgFullId, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgUnFreezeOrg msgUnFreezeOrg = new MsgUnFreezeOrg(orgFullId, sender.getKeyInfo().getAddress(), effectiveTime, voteEndTime);
        return submitter.submit(msgUnFreezeOrg, sender, txConfig);
    }

    //修改组织管理员
    public TxResponse changeOrgAdmin(Account sender, String orgFullId, String newAdminAddress) throws Exception {
        return changeOrgAdmin(sender, orgFullId, newAdminAddress, false, 0, null, null);
    }

    public TxResponse changeOrgAdmin(Account sender, String orgFullId, String newAdminAddress, Boolean isNwAdmin, Integer power, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgChangeOrgAdmin msgChangeOrgAdmin = new MsgChangeOrgAdmin(orgFullId, newAdminAddress, isNwAdmin ? 1 : 0, power, sender.getKeyInfo().getAddress(), effectiveTime, voteEndTime);
        return submitter.submit(msgChangeOrgAdmin, sender, txConfig);
    }


    //添加角色
    public TxResponse addRole(Account sender, String roleId, String baseRoleId, String orgFullId) throws Exception {
        MsgAddRole msgAddRole = new MsgAddRole(roleId, baseRoleId, orgFullId, sender.getKeyInfo().getAddress());
        return submitter.submit(msgAddRole, sender, txConfig);
    }

    //删除角色
    public TxResponse removeRole(Account sender, String orgFullId, String roleId) throws Exception {
        MsgRevokeRole msgRevokeRole = new MsgRevokeRole(orgFullId, roleId, sender.getKeyInfo().getAddress());
        return submitter.submit(msgRevokeRole, sender, txConfig);
    }

    //新增或修改权限
    public TxResponse savePermission(Account sender, String resource, PermissionPolicy policy, ExpElement[] expElements, Long effectiveTime, Long voteEndTime) throws Exception {
        effectiveTime = effectiveTime != null ? effectiveTime : 0L;
        voteEndTime = voteEndTime != null ? voteEndTime : 0L;
        MsgSavePermission msgSavePermission = new MsgSavePermission(resource, policy.getPolicy(), expElements, sender.getKeyInfo().getAddress(), effectiveTime, voteEndTime);
        return submitter.submit(msgSavePermission, sender, txConfig);
    }

    //查看账号列表
    public List<AccountInfo> queryMemberAccounts() throws WrongQueryParamException {
        return querier.abciQuery(ABCIPath.MEMBER_ACCOUNTS).list(AccountInfo.class);
    }

    //查看账户详情
    public AccountInfo queryMemberAccounts(String address) throws WrongQueryParamException {
        return querier.abciQuery(String.format(ABCIPath.MEMBER_ACCOUNT, address)).singleResult(AccountInfo.class);
    }

    //查看组织列表
    public List<OrgInfo> queryOrgs() throws WrongQueryParamException {
        return querier.abciQuery(ABCIPath.MEMBER_ORGS).list(OrgInfo.class);
    }

    //查看组织详情
    public OrgInfo queryOrgs(String orgFullId) throws WrongQueryParamException {
        return querier.abciQuery(String.format(ABCIPath.MEMBER_ORG, orgFullId)).singleResult(OrgInfo.class);
    }

    //查看权限列表
    public List<PermissionInfo> queryPermissions() throws WrongQueryParamException {
        return querier.abciQuery(ABCIPath.MEMBER_PERMISSIONS).list(PermissionInfo.class);
    }

    //查看权限详情
    public PermissionInfo queryPermissions(String resource) throws WrongQueryParamException {
        return querier.abciQuery(String.format(ABCIPath.MEMBER_PERMISSION, resource)).singleResult(PermissionInfo.class);
    }

    //检查用户权限
    public CheckPermissionResult checkPermission(String resource, String address) throws WrongQueryParamException {
        return querier.abciQuery(String.format(ABCIPath.MEMBER_CHECKPERMISSION, resource, address)).singleResult(CheckPermissionResult.class);
    }

    //查询角色列表
    public List<RoleInfo> queryRoles() throws WrongQueryParamException {
        return querier.abciQuery(ABCIPath.MEMBER_ROLES).list(RoleInfo.class);
    }

    //查询角色详情
    public RoleInfo queryRoles(String roleFullId) throws WrongQueryParamException {
        return querier.abciQuery(String.format(ABCIPath.MEMBER_ROLE, roleFullId)).singleResult(RoleInfo.class);
    }

    /*******************************************************************************************************************************************************************/
    /*
     * 合约相关操作
     * */
    //部署合约
    public TxResponse instantiateContract(Account sender, String contractName, String language, File contractFile, String initMsg, PermissionPolicy policy, String executePerm, String lable) throws Exception {
        byte[] wasmBytes = ContractFileUtil.getContent(contractFile);
        return instantiateContract(sender, contractName, language, wasmBytes, initMsg, policy, executePerm, lable);
    }

    public TxResponse instantiateContract(Account sender, String contractName, String language, byte[] wasmBytes, String initMsg, PermissionPolicy policy, String executePerm, String lable) throws Exception {
        return instantiateContract(sender, contractName, language, wasmBytes, initMsg, policy, executePerm, sender.getAddress(), lable);
    }

    public TxResponse instantiateContract(Account sender, String contractName, String language, byte[] wasmBytes, String initMsg, PermissionPolicy policy, String executePerm, String adminAddress, String lable) throws Exception {
        Msg msgInstantiateContract = null;
        switch (this.sysConfig.getVersion()) {
            case Version.SEQUENCE:
                msgInstantiateContract = new MsgInstantiateContractV1(sender.getKeyInfo().getAddress(), wasmBytes, executePerm, language, policy.getPolicy(), adminAddress, lable, contractName, initMsg.getBytes());
                break;
            default:
                msgInstantiateContract = new MsgInstantiateContract(sender.getKeyInfo().getAddress(), wasmBytes, executePerm, language, policy.getPolicy(), adminAddress, lable, contractName, initMsg);
                break;
        }
        return submitter.submit(msgInstantiateContract, sender, txConfig);
    }

    //升级合约
    public TxResponse migrateContract(Account sender, String contractName, File contractFile, String migrateMsg, PermissionPolicy policy, String executePerm) throws Exception {
        byte[] wasmBytes = ContractFileUtil.getContent(contractFile);
        return migrateContract(sender, contractName, wasmBytes, migrateMsg, policy, executePerm);
    }

    //升级合约
    public TxResponse migrateContract(Account sender, String contractName, byte[] wasmBytes, String migrateMsg, PermissionPolicy policy, String executePerm) throws Exception {
        Msg msgMigrateContract = null;
        switch (this.sysConfig.getVersion()) {
            case Version.SEQUENCE:
                msgMigrateContract = new MsgMigrateContractV1(sender.getKeyInfo().getAddress(), sender.getKeyInfo().getAddress(), contractName, wasmBytes, executePerm, policy.getPolicy(), migrateMsg.getBytes());
                break;
            default:
                msgMigrateContract = new MsgMigrateContract(sender.getKeyInfo().getAddress(), contractName, wasmBytes, executePerm, policy.getPolicy(), migrateMsg);
                break;
        }
        return submitter.submit(msgMigrateContract, sender, txConfig);
    }

    //执行合约
    public TxResponse executeContract(Account sender, String contractName, String functionName, String paramsJson) throws Exception {
        JSONObject params = new JSONObject();
        params.put(functionName, JSONObject.parseObject(paramsJson));
        String execParams = encodeStringToUtf8(params.toString(SerializerFeature.MapSortField));
        Msg msgExecuteContract = null;
        switch (this.sysConfig.getVersion()) {
            case Version.SEQUENCE:
                msgExecuteContract = new MsgExecuteContractV1(contractName, sender.getKeyInfo().getAddress(), execParams);
                break;
            default:
                msgExecuteContract = new MsgExecuteContract(contractName, sender.getKeyInfo().getAddress(), execParams);
                break;
        }
        return submitter.submit(msgExecuteContract, sender, txConfig);
    }

    public TxResponse executeCrossContract(Account sender, String contractName, String functionName, String paramsJson, String destChainId, String successContractName, String successCallback, String failContractName, String failCallback) throws Exception {
        JSONObject params = new JSONObject();
        params.put(functionName, JSON.parseObject(paramsJson));
        String execParams = encodeStringToUtf8(params.toString(SerializerFeature.MapSortField));
        Msg msgExecuteContract = null;
        switch (this.sysConfig.getVersion()) {
            case Version.SEQUENCE:
                msgExecuteContract = new MsgExecuteContractV1(contractName, sender.getKeyInfo().getAddress(), execParams);
                break;
            default:
                msgExecuteContract = new MsgExecuteContract(contractName, sender.getKeyInfo().getAddress(), execParams);
                break;
        }
        byte[] msgBytes = Codec.getAmino().marshalBinaryLengthPrefixed(msgExecuteContract);
        MsgCrossFromOrigin msgCrossFromOrigin = new MsgCrossFromOrigin(sender.getKeyInfo().getAddress(), destChainId, "tx", msgBytes, null, null, GenesisUtils.UTCDateFormat.format(new Date()));
        return submitter.submit(msgCrossFromOrigin, sender, txConfig);
    }

    public String encodeStringToUtf8(String str) throws Exception {
        String encode = "UTF-8";
        if (str.equals(new String(str.getBytes(), encode))) {
//            log.info("code type is: " + encode);
            return new String(str.getBytes(encode), "UTF-8");
        }
        encode = "GB2312";
        if (str.equals(new String(str.getBytes(), encode))) {
//            log.info("code type is: " + encode);
            return new String(str.getBytes(encode), "UTF-8");
        }
        encode = "ISO-8859-1";
        if (str.equals(new String(str.getBytes(), encode))) {
//            log.info("code type is: " + encode);
            return new String(str.getBytes(encode), "UTF-8");
        }
        encode = "GBK";
        if (str.equals(new String(str.getBytes(), encode))) {
//            log.info("code type is: " + encode);
            return new String(str.getBytes(encode), "UTF-8");
        }
        encode = "GB18030";
        if (str.equals(new String(str.getBytes(), encode))) {
//            log.info("code type is: " + encode);
            return new String(str.getBytes(encode), "UTF-8");
        }
        encode = "Big5";
        if (str.equals(new String(str.getBytes(), encode))) {
//            log.info("code type is: " + encode);
            return new String(str.getBytes(encode), "UTF-8");
        }
        encode = "Unicode";
        if (str.equals(new String(str.getBytes(), encode))) {
//            log.info("code type is: " + encode);
            return new String(str.getBytes(encode), "UTF-8");
        }
        encode = "ASCII";
        if (str.equals(new String(str.getBytes(), encode))) {
//            log.info("code type is: " + encode);
            return new String(str.getBytes(encode), "UTF-8");
        }
        return null;
    }

    //销毁合约
    public TxResponse destroyContract(Account sender, String contractName) throws Exception {
        MsgDestroyContract msgDestroyContract = new MsgDestroyContract(sender.getKeyInfo().getAddress(), contractName);
        return submitter.submit(msgDestroyContract, sender, txConfig);
    }

    //修改合约权限
    public TxResponse updateContractPermission(Account sender, String contractName, String newAdmin, PermissionPolicy policy, String executePerm) throws Exception {
        MsgUpdatePermission msgUpdatePermission = new MsgUpdatePermission(sender.getKeyInfo().getAddress(), newAdmin, contractName, policy.getPolicy(), executePerm);
        return submitter.submit(msgUpdatePermission, sender, txConfig);
    }

    //调用合约查询接口
    public String queryContract(String contractName, Map<String, Object> funcParams) throws WrongQueryParamException {
        return querier.abciQuery(String.format(ABCIPath.WASM_CONTRACT, contractName), funcParams).singleResult(String.class);
    }
    public String queryContract(String contractName, String functionName, Map<String, Object> params) throws WrongQueryParamException {
        JSONObject queryparams = new JSONObject();
        queryparams.put(functionName, params);
        return queryContract(contractName, queryparams);
    }
    public String queryContract(String contractName, String functionName, String paramsJson) throws WrongQueryParamException {
        return queryContract(contractName, functionName, JSON.parseObject(paramsJson));
    }

    //查看合约列表
    public List<ContractInfo> queryContractInfos() throws WrongQueryParamException {
        return querier.abciQuery(ABCIPath.WASM_CONTRACT_INFOS).list(ContractInfo.class);
    }

    //查看合约详情
    public ContractInfo queryContractInfo(String contractName) throws WrongQueryParamException {
        return querier.abciQuery(String.format(ABCIPath.WASM_CONTRACT_INFO, contractName)).singleResult(ContractInfo.class);
    }

    //查看合约历史
    public List<ContractHistory> queryContractHistory(String contractName) throws WrongQueryParamException {
        return querier.abciQuery(String.format(ABCIPath.WASM_CONTRACT_HISTORY, contractName)).list(ContractHistory.class);
    }

    /*******************************************************************************************************************************************************************/
    //poa
    //创建validator
    public TxResponse createValidator(Account sender, Description description, String pubkey, Integer power, Long effectiveTime, Long voteEndTime) throws Exception {
        MsgCreateValidator msgCreateValidator = new MsgCreateValidator(description, sender.getKeyInfo().getAddress(), pubkey, power, effectiveTime, voteEndTime);
        return submitter.submit(msgCreateValidator, sender, txConfig);
    }

    //编辑validator
    public TxResponse editValidator(Account sender, String validatorAddress, Integer power, Long effectiveTime, Long voteEndTime) throws Exception {
        MsgEditValidator msgEditValidator = new MsgEditValidator(validatorAddress, power, sender.getKeyInfo().getAddress(), effectiveTime, voteEndTime);
        return submitter.submit(msgEditValidator, sender, txConfig);
    }

    //查询验证者
    public List<ValidatorInfo> queryValidators() throws WrongQueryParamException {
        return querier.abciQuery(ABCIPath.POA_VALIDATORS).list(ValidatorInfo.class);
    }

    //查询验证者
    public ValidatorInfo queryValidators(String validatorAddress) throws WrongQueryParamException {
        Map<String, Object> params = new HashMap<>();
        params.put("ValidatorAddr", validatorAddress);
        return querier.abciQuery(ABCIPath.POA_VALIDATOR, params).singleResult(ValidatorInfo.class);
    }

    /*******************************************************************************************************************************************************************/
    //gov
    //投票
    public TxResponse voteProposal(Account sender, String proposalId, VoteType voteType) throws Exception {
        MsgVote msgVote = new MsgVote(proposalId, voteType.getType(), sender.getKeyInfo().getAddress());
        return submitter.submit(msgVote, sender, txConfig);
    }

    //撤销提案1
    public TxResponse voidProposal(Account sender, String proposalId) throws Exception {
        MsgVoidProposal msgVoidProposal = new MsgVoidProposal(proposalId, sender.getKeyInfo().getAddress());
        return submitter.submit(msgVoidProposal, sender, txConfig);
    }

    //查询提案列表
    public List<ProposalInfo> queryProposals() throws WrongQueryParamException {
        return querier.abciQuery(ABCIPath.GOV_PROPOSALS).list(ProposalInfo.class);
    }

    public ProposalInfo queryProposals(String proposalId) throws WrongQueryParamException {
        return querier.abciQuery(String.format(ABCIPath.GOV_PROPOSAL, proposalId)).singleResult(ProposalInfo.class);
    }

    /*******************************************************************************************************************************************************************/
    //bank相关操作
    public TxResponse transfer(Account sender, String to, String denom, String amount) throws Exception {
        MsgTransfer msgTransfer = new MsgTransfer(sender.getKeyInfo().getAddress(), to, new Token[]{new Token(denom, amount)});
        return submitter.submit(msgTransfer, sender, new TxConfig("", 1000000000L, TxMode.BLOCK));
    }

    //查看账号nonce 和用户余额
    public AuthAccount queryAuthAccount(String address) throws WrongQueryParamException {
        Map<String, Object> params = new HashMap<>();
        params.put("Address", address);
        return querier.abciQuery(ABCIPath.AUTH_ACCOUNTS, params).singleResult(AuthAccount.class);
    }

    public TxInfo queryTx(String hash) throws WrongQueryParamException {
        return querier.queryTx(hash);
    }

    public List<TxInfo> queryTxs(Long height) throws WrongQueryParamException {
        return querier.queryTxs(height);
    }
    public List<TxInfo> queryTxs(int page, int pageSize) throws WrongQueryParamException {
        return querier.queryTxs(page, pageSize);
    }

    public BlockInfo queryBlock(String height) throws WrongQueryParamException {
        return querier.queryBlock(height);
    }

    public BlockInfo queryBlockByHash(String blockHash) throws WrongQueryParamException {
        return querier.queryBlockByHash(blockHash);
    }

    public BlockMetas queryBlock(String minHeight, String maxHeight) throws WrongQueryParamException {
        return querier.queryBlock(minHeight, maxHeight);
    }

    //查询tendermint的验证人信息
    public String queryTendermintValidators() throws WrongQueryParamException {
        JSONObject jsonObject = querier.execute(RPCPath.DUMP_CONSENSUS_STATE, new JSONObject(), JSONObject.class);
        return jsonObject.getJSONObject("round_state").getJSONObject("validators").toJSONString();
    }

    /*******************************************************************************************************************************************************************/
    //celerain 相关操作
    public TxResponse putCelerain(Account sender, String key, String value) throws Exception {
        MsgCelerain msgCelerain = new MsgCelerain(key, value, sender.getKeyInfo().getAddress());
        return submitter.submit(msgCelerain, sender, txConfig);
    }

    public String queryCelerain(String key) throws Exception {
        return querier.abciQuery(String.format(ABCIPath.CELERAIN_CELERAIN, key)).stringResult();
    }

    //广播交易
    public TxResponse broadcast(String tx) {
        byte[] txBytes = Base64.getDecoder().decode(tx);
        return submitter.submit(txBytes, TxMode.SYNC);
    }

    //核心链
    //应用链注册
    public TxResponse appChainRegister(Account sender, AppChainInfo appChainInfo, long effectiveTime, long voteEndTime) throws Exception {
        MsgAppChain msgAppChain = new MsgAppChain("admin", sender.getAddress(), appChainInfo, AppChainOperationEnum.CROSS_APP_CHAIN_REGISTER.getType(), effectiveTime, voteEndTime);
        return submitter.submit(msgAppChain, sender, this.txConfig);
    }

    //查询注册的应用链信息
//    public AppChainInfo queryAppChainInfo(String chainId) throws WrongQueryParamException {
//        return querier.abciQuery(String.format(ABCIPath.ICCP_APPCHAININFO, chainId)).singleResult(AppChainInfo.class);
//    }
    public AppChainInfo queryAppChainInfo(String chainId) throws WrongQueryParamException {
        String storeKey = "appchain-" + chainId;
        ABCIResponse abciResponse = querier.abciQuery(ABCIPath.ICCP_APPCHAININFO_STORE, storeKey.getBytes());
        if (abciResponse == null || StringUtils.isBlank(abciResponse.getResponse().getValue())) {
            return null;
        }
        byte[] decode = Base64.getDecoder().decode(abciResponse.getResponse().getValue());
        AppChainInfo appChainInfo = Codec.getAmino().unmarshalBinaryLengthPrefixed(decode, AppChainInfo.class);
        return appChainInfo;
    }

    public Boolean health() {
        try {
            querier.execute(HEALTH, new JSONObject(), Object.class);
            return true;
        } catch (WrongQueryParamException e) {
            e.printStackTrace();
        }
        return false;
    }

    /*******************************************************************************************************************************************************************/
    public BlockInfo getLatestBlock() throws WrongQueryParamException {
        Object result = querier.query("status");
        String latestHeight = JSON.parseObject(JSON.toJSONString(result)).getJSONObject("sync_info").getString("latest_block_height");
        return this.queryBlock(latestHeight);
    }

    public KeyInfo generateKeyInfo(AlgorithmType algorithmType, String mainPrefix) throws Exception {
        String mnemonic = Mnemonic.generateMnemonic();
        return new KeyInfo(mnemonic, algorithmType, mainPrefix);
    }
}
